import { EventEmitter } from 'events';
import { ConfigManager } from './core/config';
import { ConnectionPool } from './core/connection-pool';
import { Producer, ReliableProducer } from './core/producer';
import { Consumer, ConcurrentConsumer } from './core/consumer';
import { WorkQueue, PubSub, Router, RPC } from './patterns';
import { MiddlewarePipeline } from './middleware';
import { ConnectionPoolMonitor, PoolHealthStatus } from './monitoring/connection-pool-monitor';
import { RabbitMQConfig, Logger, MessageHandler, ConnectionPoolConfig } from './types';
import { createLogger } from './utils/logger';

/**
 * Main RabbitMQ client class that provides a high-level interface
 */
export class RabbitMQClient extends EventEmitter {
  private readonly config: ConfigManager;
  private readonly connectionPool: ConnectionPool;
  private readonly logger: Logger;
  private readonly middlewarePipeline: MiddlewarePipeline;
  private readonly poolMonitor: ConnectionPoolMonitor;
  private isInitialized = false;

  constructor(
    config?: Partial<RabbitMQConfig> | ConfigManager,
    poolConfig?: Partial<ConnectionPoolConfig>,
    logger?: Logger
  ) {
    super();

    this.config = config instanceof ConfigManager ? config : new ConfigManager();
    if (config && !(config instanceof ConfigManager)) {
      this.config.updateConfig(config);
    }

    this.logger = logger ?? createLogger('RabbitMQClient');
    this.connectionPool = new ConnectionPool(this.config.getRabbitMQConfig(), poolConfig || {}, this.logger);
    this.middlewarePipeline = new MiddlewarePipeline();

    // 初始化连接池监控器
    this.poolMonitor = new ConnectionPoolMonitor(this.connectionPool, {
      enabled: true,
      checkInterval: 30000, // 30秒检查一次
    }, this.logger);

    // Forward connection pool events
    this.connectionPool.on('error', (error) => {
      this.logger.warn('Connection pool error:', error.message);
      // 只在有监听器时才转发错误事件
      if (this.listenerCount('error') > 0) {
        this.emit('error', error);
      }
    });
    this.connectionPool.on('connectionCreated', (connection) => this.emit('connectionCreated', connection));
    this.connectionPool.on('connectionRemoved', (connection) => this.emit('connectionRemoved', connection));
    this.connectionPool.on('poolScaled', (event) => this.emit('poolScaled', event));

    // Forward monitoring events
    this.poolMonitor.on('alert', (alert) => this.emit('poolAlert', alert));
    this.poolMonitor.on('healthCheck', (data) => this.emit('poolHealthCheck', data));
  }

  /**
   * Initialize the client
   */
  async initialize(): Promise<void> {
    if (this.isInitialized) {
      return;
    }

    try {
      // Test connection by getting one from the pool
      const connection = await this.connectionPool.getConnection();
      await this.connectionPool.returnConnection(connection);
      
      this.isInitialized = true;
      this.logger.info('RabbitMQ client initialized successfully');
      this.emit('initialized');
    } catch (error) {
      this.logger.error('Failed to initialize RabbitMQ client', error as Error);
      throw error;
    }
  }

  /**
   * Create a producer
   */
  createProducer(reliable = false): Producer {
    if (reliable) {
      return new ReliableProducer(this.connectionPool, true);
    }
    return new Producer(this.connectionPool);
  }

  /**
   * Create a consumer
   */
  createConsumer(handler: MessageHandler, concurrent = false, concurrency = 5): Consumer {
    if (concurrent) {
      return new ConcurrentConsumer(this.connectionPool, handler, concurrency);
    }
    return new Consumer(this.connectionPool, handler);
  }

  /**
   * Create a work queue
   */
  createWorkQueue(queueName: string): WorkQueue {
    return new WorkQueue(this.connectionPool, queueName, {}, this.logger);
  }

  /**
   * Create a pub/sub
   */
  createPubSub(exchangeName: string): PubSub {
    return new PubSub(this.connectionPool, exchangeName, {}, this.logger);
  }

  /**
   * Create a router
   */
  createRouter(exchangeName: string): Router {
    return new Router(this.connectionPool, exchangeName, {}, this.logger);
  }

  /**
   * Create an RPC client/server
   */
  createRPC(serviceName: string): RPC {
    return new RPC(this.connectionPool, serviceName, this.logger);
  }

  /**
   * Get the middleware pipeline
   */
  getMiddlewarePipeline(): MiddlewarePipeline {
    return this.middlewarePipeline;
  }

  /**
   * Get connection pool statistics
   */
  getConnectionPoolStats() {
    return this.connectionPool.getStats();
  }

  /**
   * Get connection pool statistics
   */
  getConnectionStats(): {
    totalConnections: number;
    availableConnections: number;
    busyConnections: number;
    poolSize: number;
  } {
    return this.connectionPool.getStats();
  }

  /**
   * Get current configuration
   */
  getConfig(): RabbitMQConfig {
    return this.config.getRabbitMQConfig();
  }

  /**
   * Update configuration
   */
  updateConfig(updates: Partial<RabbitMQConfig>): void {
    this.config.updateConfig(updates);
  }

  /**
   * Check if client is initialized
   */
  isReady(): boolean {
    return this.isInitialized;
  }

  /**
   * Get extended connection pool statistics with monitoring data
   */
  getExtendedConnectionPoolStats() {
    return this.connectionPool.getExtendedStats();
  }

  /**
   * Get connection pool health status
   */
  getPoolHealthStatus(): PoolHealthStatus {
    return this.poolMonitor.getHealthStatus();
  }

  /**
   * Get connection pool monitoring metrics summary
   */
  getPoolMetricsSummary(duration?: number) {
    return this.poolMonitor.getMetricsSummary(duration);
  }

  /**
   * Get connection pool alert history
   */
  getPoolAlertHistory(duration?: number) {
    return this.poolMonitor.getAlertHistory(duration);
  }

  /**
   * Reset connection pool statistics
   */
  resetPoolStats(): void {
    this.connectionPool.resetStats();
  }

  /**
   * Start connection pool monitoring (if not already started)
   */
  startPoolMonitoring(): void {
    this.poolMonitor.startMonitoring();
  }

  /**
   * Stop connection pool monitoring
   */
  stopPoolMonitoring(): void {
    this.poolMonitor.stopMonitoring();
  }

  /**
   * Close the client and all connections
   */
  async close(): Promise<void> {
    try {
      this.poolMonitor.destroy();
      await this.connectionPool.shutdown();
      this.isInitialized = false;
      this.logger.info('RabbitMQ client closed');
      this.emit('closed');
    } catch (error) {
      this.logger.error('Error closing RabbitMQ client', error as Error);
      throw error;
    }
  }

  /**
   * Create a client with default configuration
   */
  static create(
    config?: Partial<RabbitMQConfig>,
    poolConfig?: Partial<ConnectionPoolConfig>,
    logger?: Logger
  ): RabbitMQClient {
    return new RabbitMQClient(config, poolConfig, logger);
  }

  /**
   * Create a client from configuration file
   */
  static fromConfigFile(
    configFile: string,
    poolConfig?: Partial<ConnectionPoolConfig>,
    logger?: Logger
  ): RabbitMQClient {
    const configManager = new ConfigManager(configFile);
    return new RabbitMQClient(configManager, poolConfig, logger);
  }

  /**
   * Create a client from environment variables
   */
  static fromEnvironment(
    envPrefix = 'RABBITMQ_',
    poolConfig?: Partial<ConnectionPoolConfig>,
    logger?: Logger
  ): RabbitMQClient {
    const configManager = new ConfigManager(undefined, envPrefix);
    return new RabbitMQClient(configManager, poolConfig, logger);
  }
}

// Default client instance
let defaultClient: RabbitMQClient | undefined;

/**
 * Get or create the default client instance
 */
export const getDefaultClient = (
  config?: Partial<RabbitMQConfig>,
  poolConfig?: Partial<ConnectionPoolConfig>,
  logger?: Logger
): RabbitMQClient => {
  if (!defaultClient) {
    defaultClient = new RabbitMQClient(config, poolConfig, logger);
  }
  return defaultClient;
};

/**
 * Set the default client instance
 */
export const setDefaultClient = (client: RabbitMQClient): void => {
  defaultClient = client;
};

/**
 * Close the default client instance
 */
export const closeDefaultClient = async (): Promise<void> => {
  if (defaultClient) {
    await defaultClient.close();
    defaultClient = undefined;
  }
};
